const axios = require('axios')
const rewire = require('rewire')
const cache = rewire('../common/cache.cjs')
const {Lock} = rewire('../common/lock.cjs')
const config = rewire('../config.cjs')
const kdl = require('kdl/kdl')
const {sleep} = rewire('../common/utils.cjs')
const R = require('ramda')
const { HttpsProxyAgent } = require("https-proxy-agent")
const {getLogger} = rewire('../common/logger.cjs')


const logger = getLogger('proxy')

const Client = kdl.client
const Auth = kdl.auth
const auth = new Auth(config.kdlProxy.proxySecretId, config.kdlProxy.proxySecretKey)
const client = new Client(auth)


const CacheKey = 'proxy_'

async function getRamdomProxy() {
  let proxyStr = await getFromCache()

  if (proxyStr) {
    return proxyStr
  }

  const plist = await getKdlProxyList(1)
  proxyStr = plist[0]

  const validObj = await client.getDpsValidTime(plist, 'hmacsha1');
  await setToCache(proxyStr, validObj[proxyStr])

  return proxyStr
}

/** 创建上下文 */
function makeProxyContext(num = 5) {
  return {
    index: 0,
    num: num,
    cacheData: new Array(num).fill(null)
  }
}


function proxyCreator(proxyFetcher) {
  const proxyLock = new Lock()
  return async proxyContext => {
    const unlock = await proxyLock.lock()
    if (proxyContext.index >= proxyContext.num) {
      proxyContext.index = 0
    }

    const objlog = proxyContext.cacheData[proxyContext.index]
    logger.info(objlog ? objlog['value'] : '空IP')
  
    let proxyStr = await getFromProxyContext(proxyContext)
  
    if (proxyStr == null) {
      proxyStr = await proxyFetcher(proxyContext)
      logger.info(proxyStr)
    }
    proxyContext.index++
    await unlock()
    return proxyStr
  }
}

function makeKdlProxyFetcher() {
  return async function(proxyContext) {
    const plist = await getKdlProxyList(1)
    proxyStr = plist[0]
  
    const validObj = await getKdlValidTime(plist);
    await setToProxyContext(proxyContext, proxyStr, validObj[0][proxyStr])
    return proxyStr
  }
}

function makeDouyaProxyFetcher(mode) {
  return async function(proxyContext) {
    const plist = await getDouyaProxyList(1, mode)
    proxyStr = plist[0]
  
    const validObj = await getDouyaValidTime(plist, mode);
    await setToProxyContext(proxyContext, proxyStr, validObj[0][proxyStr])

    await sleep(110)
    return proxyStr
  }
}

const kdlProxyFetcher = makeKdlProxyFetcher()
const douyaProxyFetcher = makeDouyaProxyFetcher(1)

const getKdlProxy = proxyCreator(kdlProxyFetcher)
const getDouyaProxy = proxyCreator(douyaProxyFetcher)

async function getFromProxyContext(proxyContext) {
  const obj = proxyContext.cacheData[proxyContext.index]
  if (obj == null) {
    return null
  }
  const current = new Date().getTime()
  const validTime = obj.validTime
  const cacheTime = obj.cacheTime
  if ((current - cacheTime) >= validTime * 1000) {
    return null
  }
  return obj.value
}

async function setToProxyContext(proxyContext, value, validTime) {
  let obj = {
    validTime, value, cacheTime: new Date().getTime()
  }
  proxyContext.cacheData[proxyContext.index] = obj
}

// 从redis读取代理IP
async function getFromCache() {
  return await cache.getCache(CacheKey)
}

// 设置redis代理IP
async function setToCache(proxyStr, sec) {
  await cache.saveCache(CacheKey, proxyStr, sec)
}


// douya 获取IP有效时间，返回的是对象，key是IP，value是时长秒
// mode:1短时间，2长时间
async function getDouyaValidTime(ipList, mode = 1) {
  const lifetime = (mode == 1) ? 1 : 15
  return ipList.map(item => {
    let rs = {}
    rs[item] = 60 * lifetime - 10
    return rs
  })
}


// douya 获取代理IP列表，返回是列表['xxx.xxx.xxx.xxx:xxx']
// nums是一次性取的个数，mode:1短时间，2长时间
async function getDouyaProxyList(num, mode = 1) {
  const url = mode == 2 ? 
  `https://api.douyadaili.com/proxy/?service=GetIp&authkey=rBiIPJEq9ho7J1uLbJYp&num=${num}&lifetime=15`
  :
  `https://api.douyadaili.com/proxy/?service=GetUnl&authkey=rBiIPJEq9ho7J1uLbJYp&num=${num}&lifetime=1`

  const resp = await axios({
    url,
    method: 'get'
  })
  const data = resp.data
  const rs = data.split('\n')
  const reg = /((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?):(\d{1,5})/
  if (R.isEmpty(rs) || !reg.test(rs[0])) {
    throw new Error("获取IP失败: " + data)
  }
  return rs
}


// kdl平台 获取IP有效时间，返回的是对象，key是IP，value是时长秒
async function getKdlValidTime(ipList) {
  return await client.getDpsValidTime(ipList, 'hmacsha1');
}


// kdl平台 获取代理IP列表，返回是列表['xxx.xxx.xxx.xxx:xxx']
async function getKdlProxyList(num) {
  params = { format: 'json' }
  try {
    return await client.getDpsProxy(num, 'hmacsha1', params)
  } catch(e) {
    throw new Error("kdl代理: " + e.message)
  }
}


let context = null

/**
 * 
 * @returns 返回快代理的代理配置 https://www.kuaidaili.com/
 */
 async function getKdlProxyConfig(num = 1, refresh = false) {
  const username = config.kdlProxy.proxyUsername;
  const password = config.kdlProxy.proxyPassword;
  if (context == null) {
    context = makeProxyContext(num)
  }
  if (refresh && context != null) {
    await kdlProxyFetcher(context)
  }
  const ipPort = await getKdlProxy(context);
  return {
      httpAgent: new HttpsProxyAgent(`http://${username}:${password}@${ipPort}`),
      httpsAgent: new HttpsProxyAgent(`http://${username}:${password}@${ipPort}`)
  }
}

/**
 * 
 * @returns 返回豆芽的代理配置 https://h.douyaruanjian.com/
 */
async function getDouyaProxyConfig(num = 1, refresh = false) {
  const username = config.douyaProxy.proxyUsername;
  const password = config.douyaProxy.proxyPassword;
  if (context == null) {
    context = makeProxyContext(num)
  }

  // 手动刷新当前上下文的IP(当IP无效时需要试用)
  if (refresh && context != null) {
    await douyaProxyFetcher(context)
  }
  const ipPort = await getDouyaProxy(context)
  return {
    httpAgent: new HttpsProxyAgent(`http://${username}:${password}@${ipPort}`),
    httpsAgent: new HttpsProxyAgent(`http://${username}:${password}@${ipPort}`)
  }
}


module.exports = { getRamdomProxy, getKdlProxyConfig, getDouyaProxyConfig, makeProxyContext }